//== SAGITTARIUS ============================================================================

//== BEGINNING OF CODE ===============================================================

#include "sagittarius/unicode/CUnicode.h"
#include <assert.h>
#include <malloc.h>
#include <string.h>

///Convert buffer
int UTF8_ConvertBuffer(const char* in, UnicodeCChar* out)
{
    int total=0;
    int n, i;
    
    while(*in != 0x00)
    {
        if(*in & 0x80)
        {
            //more than one byte
            for(n = 2; n<=4; ++n)
            {
                if(!((*in << n) & 0x80))
                    break;
            }
            if((*in << n) & 0x80)
            {
                fprintf(stderr, "UTF8_ConvertBuffer: Unrecognized start byte (possibly encoding for forbidden five or six-byte sequence)\n");
                return -1;
            }
            *out = (UnicodeCChar)(*in & ((0x01 << (7 - n)) - 1));
            for(i=1; i!=n; ++i)
            {
                ++in;
                //continuation byte
                if((*in & 0xC0) != 0x80)
                {
                    fprintf(stderr, "UTF8_ConvertBuffer: Expected continuation byte\n");
                    return -1;
                }
                //get data
                *out = (*out << 6) | (*in & 0x3F);
            }
            ++in;
            ++out;
            ++total;
        }
        else
        {
            //one byte
            *out = *in;
            ++out;
            ++in;
            ++total;
        }
    }
    //at this point, *in is 0x00, so add null-terminating character to output buffer
    *out = 0x00;
    return total;
}

//convert to string
int UCEncode(const UnicodeCChar* buf, char* out)
{
    size_t n_bytes;
    
    while(*buf != 0x00)
    {
        n_bytes = UCgetNBytes(*buf);
        switch(n_bytes)
        {
            case 1:
                *out = (char)(*buf & 0x7F);
                ++out;
                ++buf;
                break;
            case 2:
                *out = (char)((*buf >> 6) & 0x1F) | 0xC0;
                ++out;
                *out = (char)(*buf & 0x3F) | 0x80;
                ++out;
                ++buf;
                break;
            case 3:
                *out = (char)((*buf >> 12) & 0x0F) | 0xE0;
                ++out;
                *out = (char)((*buf >> 6) & 0x3F) | 0x80;
                ++out;
                *out = (char)(*buf & 0x3F) | 0x80;
                ++out;
                ++buf;
                break;
            case 4:
                *out = (char)((*buf >> 18) & 0x07) | 0xF0;
                ++out;
                *out = (char)((*buf >> 12) & 0x3F) | 0x80;
                ++out;
                *out = (char)((*buf >> 6) & 0x3F) | 0x80;
                ++out;
                *out = (char)(*buf & 0x3F) | 0x80;
                ++out;
                ++buf;
                break;
            default:
                assert(0 && "printUnicodeBuf: Invalid number of bytes");
        }
    }
    //null-terminating character
    *out = 0x00;
    return 0;
}

///Print buffer
int printUnicodeBuf(const UnicodeCChar* buf)
{
    size_t len;
    char* tmp;
    
    len = UC_UTF8BufferLen(buf);
    tmp = (char*)malloc(len*sizeof(UnicodeCChar)); //FIXME: should it be sizeof(char)?
    assert(tmp);
    
    UCEncode(buf,tmp);
    printf("%s", tmp);
    
    free(tmp);
    return 0;
}

//print buf with length
int fprintUnicodeBufl(FILE* stream, const UnicodeCChar* buf, const int l)
{
    size_t len = l;
    char* tmp;
    UnicodeCChar* u = (UnicodeCChar*)malloc((len+1)*sizeof(UnicodeCChar));
    assert(l > 0);
    memcpy(u,buf,len*sizeof(UnicodeCChar));
    u[len] = 0x00;
    
    len = UC_UTF8BufferLen(u);
    tmp = (char*)malloc(len*sizeof(char));
    assert(tmp);
    
    UCEncode(u,tmp);
    fprintf(stream, "%s", tmp);
    
    free(tmp);
    free(u);
    return 0;
}

///UTF8_ShrinkBuffer
UnicodeCChar* UTF8_ShrinkBuffer(UnicodeCChar* buf, size_t new_size)
{
    UnicodeCChar* newbuf;
    newbuf = (UnicodeCChar*)malloc(new_size*sizeof(UnicodeCChar));
    assert(newbuf != NULL);
    memcpy(newbuf, buf, new_size);
    free(buf);
    return newbuf;
}

///UCgetNBytes
int UCgetNBytes(UnicodeCChar chr)
{
    //printf("UCgetNBytes: char = 0x%x\n", chr);
    if(chr <= 0x007F)
        return 1;
    else if(chr <= 0x07FF)
        return 2;
    else if(chr <= 0xFFFF)
        return 3;
    else if(chr <= 0x1FFFFF)
        return 4;
    else if(chr <= 0x3FFFFFF)
        return 5;
    else if(chr <= 0x7FFFFFFF)
        return 6;
    else
        assert(0 && "UCgetNBytes: Unrecognized codepoint");
}

//get buffer length
size_t UCBufferLen(const UnicodeCChar* buf)
{
    size_t n_bytes;
    n_bytes = 0;
    while(*buf != 0x00)
    {
        ++n_bytes;
        ++buf;
    }
    ++n_bytes; //get null-terminating character
    return n_bytes;
}

//get buffer length
size_t UC_UTF8BufferLen(const UnicodeCChar* buf)
{
    size_t n_bytes;
    n_bytes = 0;
    while(*buf != 0x00)
    {
        n_bytes += UCgetNBytes(*buf);
        ++buf;
    }
    ++n_bytes; //get null-terminating character
    return n_bytes;
}
